package com.uzai.console.service.merchant.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.uzai.common.enums.SysMsgTypeEnum;
import com.uzai.common.exception.BusinessException;
import com.uzai.common.result.ResponseCode;
import com.uzai.common.utils.EsIndexName;
import com.uzai.console.common.ResponseMsg;
import com.uzai.console.common.UzaiRespVo;
import com.uzai.console.common.utils.DateUtil;
import com.uzai.console.common.utils.Tools;
import com.uzai.console.component.Ip138InfoUtil;
import com.uzai.console.config.UzaiConsoleSysConfig;
import com.uzai.console.constant.CacheKey;
import com.uzai.console.constant.ES.ElasticsearchUtil;
import com.uzai.console.dto.feign.alarm.AlarmMsgFeignDto;
import com.uzai.console.dto.log.userloginlog.AreaByLoginTimeMaxDto;
import com.uzai.console.dto.login.*;
import com.uzai.console.entity.KefuUser;
import com.uzai.console.entity.ManageSuperAccount;
import com.uzai.console.entity.Merchant;
import com.uzai.console.entity.es.EsUserLoginLog;
import com.uzai.console.enums.SmsType;
import com.uzai.console.mapper.KefuUserMapper;
import com.uzai.console.mapper.ManageSuperAccountMapper;
import com.uzai.console.mapper.MerchantMapper;
import com.uzai.console.service.alarm.AlarmEventService;
import com.uzai.console.service.feign.UzaiBroadcastFeignService;
import com.uzai.console.service.log.UserLoginLogService;
import com.uzai.console.service.merchant.LoginService;
import com.uzai.console.service.ratelimit.RatelimitService;
import com.uzai.console.service.rocketmq.NoticeMsgPushProducerService;
import com.uzai.console.service.sms.SmsService;
import com.uzai.console.vo.alarm.alarmevent.AlarmEventConfigVo;
import com.uzai.console.vo.ip138.Ip138Info;
import com.uzai.console.vo.log.EsUserLoginLogTimes;
import com.uzai.console.vo.login.LoginVo;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @Auther: jianghan
 * @Date: 2019/12/13 10:18
 * @Description:
 */
@Service
public class LoginServiceImpl implements LoginService {

    private Logger logger = LoggerFactory.getLogger(LoginServiceImpl.class);

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private MerchantMapper merchantMapper;
    @Autowired
    private KefuUserMapper kefuUserMapper;
    @Autowired
    private RestTemplate restTemplate;
//    @Autowired
//    private IpInfoUtil ipInfoUtil;
    @Autowired
    private Ip138InfoUtil ip138InfoUtil;
    @Autowired
    private ElasticsearchUtil elasticsearchUtil;
    @Autowired
    private UzaiConsoleSysConfig uzaiConsoleSysConfig;
    @Autowired
    private UserLoginLogService userLoginLogService;
    @Autowired
    private AlarmEventService alarmEventService;
    @Autowired
    private UzaiBroadcastFeignService uzaiBroadcastFeignService;
    @Autowired
    private RatelimitService ratelimitService;
    @Autowired
    private SmsService smsService;
    @Autowired
    private NoticeMsgPushProducerService noticeMsgPushProducerService;
    @Autowired
    private ManageSuperAccountMapper manageSuperAccountMapper;

    /**
     * 手机号验证码登录
     * @param loginByPhone
     * @return
     */
    @Override
    public LoginVo loginByCode(LoginByPhone loginByPhone) {

        //验证码输入错误次数超过5次，触发5分钟禁止登录机制
        String prohibitLoginByCodeErrTime = "prohibitLoginByCodeErrTime-" + loginByPhone.getMobile();
        //从缓存中获取此验证码是否存在，如果不存在，表示第一次触发，则需要发送短信提醒
        Long prohibitLoginByCodeErrTimeExpire = Tools.getLong(redisTemplate.getExpire(prohibitLoginByCodeErrTime));
        if(prohibitLoginByCodeErrTimeExpire > 0){ //表示还未到期
            String expireDate = Tools.convertSecondsToHourMinuteSecond(prohibitLoginByCodeErrTimeExpire.intValue());
            //错误则返回错误信息
            throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, "验证码尝试次数太多，请" + expireDate + "后再试");
        }

        //登录日志
        EsUserLoginLog esUserLoginLog = new EsUserLoginLog();
        esUserLoginLog.setId(String.valueOf(IdWorker.getId()));
        esUserLoginLog.setAccount(loginByPhone.getMobile());
        esUserLoginLog.setType(0);//主账号登录
        esUserLoginLog.setLoginTime(DateUtil.getNowTime());
        //IpInfoUtil.IpInfo ipInfo = ipInfoUtil.getRequestIpInfo();
//        esUserLoginLog.setIp(ipInfo.getIp());
//        esUserLoginLog.setCountry(ipInfo.getCountry());
//        esUserLoginLog.setProvince(ipInfo.getProvince());
//        esUserLoginLog.setCity(ipInfo.getCity());

        Ip138Info requestIpInfo = ip138InfoUtil.getRequestIpInfo();
        esUserLoginLog.setIp(Tools.getStr(requestIpInfo.getIp()));
        esUserLoginLog.setCountry(Tools.getStr(requestIpInfo.getCountry()));
        esUserLoginLog.setProvince(Tools.getStr(requestIpInfo.getProvince()));
        esUserLoginLog.setCity(Tools.getStr(requestIpInfo.getCity()));
        esUserLoginLog.setArea(Tools.getStr(requestIpInfo.getArea()));

        //判断输入的手机号是否正确
        if (StringUtils.isBlank(loginByPhone.getMobile()) || loginByPhone.getMobile().length()!=11){

            //登录失败
            esUserLoginLog.setLoginStatus(0);
            //登录结果
            esUserLoginLog.setLoginResult(ResponseMsg.MOBILE_ERROR);
            try {
                elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
            }catch (Exception e){
                logger.error("账号登录日志插入ES失败");
            }

            throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, ResponseMsg.MOBILE_ERROR);
        }
        //获取存在redis中的验证码
        String codeByRedis = (String) redisTemplate.opsForValue().get(String.format(SmsType.LOGIN.getDesc(), loginByPhone.getMobile()));
        //判断验证码是否过期
        if (StringUtils.isBlank(codeByRedis)){

            //登录失败
            esUserLoginLog.setLoginStatus(0);
            //登录结果
            esUserLoginLog.setLoginResult(ResponseMsg.CODE_MISS);
            try {
                elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
            }catch (Exception e){
                logger.error("账号登录日志插入ES失败");
            }

            throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, ResponseMsg.CODE_MISS);
        }

        //验证码是否正确
        if (!codeByRedis.equals(loginByPhone.getCode())){
            //2023-08-14修改，滑块验证5分钟连续输入5次都错误，禁用5分钟，并在【其他异常】中报警
            //限流判断(300秒内判断一次)
            //判断用户是否存在,不等于空才触发
            Merchant merchant = merchantMapper.selectByMobile(loginByPhone.getMobile());
            if(merchant != null){
                boolean inRateLimit = ratelimitService.inRateLimit("loginByCodeErrTime-" + loginByPhone.getMobile(),5,300);
                if (!inRateLimit){
                    //设置验证码输入错误次数超过5次禁止登录key
                    redisTemplate.opsForValue().set(prohibitLoginByCodeErrTime, loginByPhone.getMobile(), 5, TimeUnit.MINUTES);

                    //发送短信模板消息
                    smsService.smsTemplate("SMS_462645398", loginByPhone.getMobile());

                    //错误则返回错误信息
                    throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, "验证码尝试次数太多，请5分钟后再试");
                }
            }

            //登录失败
            esUserLoginLog.setLoginStatus(0);
            //登录结果
            esUserLoginLog.setLoginResult(ResponseMsg.CODE_ERR);
            try {
                elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
            }catch (Exception e){
                logger.error("账号登录日志插入ES失败");
            }

            throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, ResponseMsg.CODE_ERR);
        }else { //验证成功，手动删除redis缓存
            redisTemplate.delete(String.format(SmsType.LOGIN.getDesc(),loginByPhone.getMobile()));
        }
        //判断用户是否存在
        Merchant merchant = merchantMapper.selectByMobile(loginByPhone.getMobile());
        if (merchant != null ){
            //登录成功，生成token
            String token = UUID.randomUUID().toString();
            LoginVo loginVO = new LoginVo();
            loginVO.setMerId(merchant.getId());
            loginVO.setType(0);//主账号登录
            loginVO.setLoginToken(token);
            loginVO.setCreateTime(merchant.getCreateTime());
            loginVO.setLoginName(merchant.getMobile());
            loginVO.setMobile(merchant.getMobile());
            //将token保存到Redis缓存当中
            redisTemplate.opsForValue().set(String.format(CacheKey.LOGIN_TOKEN, token), Tools.getStrEmpty(JSONObject.toJSONString(loginVO)), CacheKey.LOGIN_TOKEN_EXPIRE, TimeUnit.SECONDS);

            //登录失败
            esUserLoginLog.setLoginStatus(1);
            esUserLoginLog.setMerId(merchant.getId());
            esUserLoginLog.setLoginToken(token);
            //登录结果
            esUserLoginLog.setLoginResult("登录成功");
            try {
                elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
            }catch (Exception e){
                logger.error("账号登录日志插入ES失败");
            }

            return loginVO;
        }else {
            //登录失败
            esUserLoginLog.setLoginStatus(0);
            //登录结果
            esUserLoginLog.setLoginResult(ResponseMsg.MERCHAT_UNREG);
            try {
                elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
            }catch (Exception e){
                logger.error("账号登录日志插入ES失败");
            }

            throw new BusinessException(ResponseCode.NOT_FOUND,ResponseMsg.MERCHAT_UNREG);
        }
    }

    /**
     * 账号密码登录
     * @param loginByPwd
     * @return
     */
    @Override
    public LoginVo loginByPwd(LoginByPwd loginByPwd) {

        //验证码输入错误次数超过5次，触发1小时禁止登录机制
        String prohibitLoginByPasswordErrTime = "prohibitLoginByPasswordErrTime-" + loginByPwd.getMobile();
        //从缓存中获取此验证码是否存在，如果不存在，表示第一次触发，则需要发送短信提醒
        Long prohibitLoginByPasswordErrTimeExpire = Tools.getLong(redisTemplate.getExpire(prohibitLoginByPasswordErrTime));
        if(prohibitLoginByPasswordErrTimeExpire > 0){ //表示还未到期
            String expireDate = Tools.convertSecondsToHourMinuteSecond(prohibitLoginByPasswordErrTimeExpire.intValue());
            //错误则返回错误信息
            throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, "密码尝试次数太多，请" + expireDate + "后再试");
        }

        //登录日志
        EsUserLoginLog esUserLoginLog = new EsUserLoginLog();
        esUserLoginLog.setId(String.valueOf(IdWorker.getId()));
        esUserLoginLog.setAccount(loginByPwd.getMobile());
        esUserLoginLog.setType(0);//主账号登录
        esUserLoginLog.setLoginTime(DateUtil.getNowTime());
//        IpInfoUtil.IpInfo ipInfo = ipInfoUtil.getRequestIpInfo();
//        esUserLoginLog.setIp(ipInfo.getIp());
//        esUserLoginLog.setCountry(ipInfo.getCountry());
//        esUserLoginLog.setProvince(ipInfo.getProvince());
//        esUserLoginLog.setCity(ipInfo.getCity());

        Ip138Info requestIpInfo = ip138InfoUtil.getRequestIpInfo();
        esUserLoginLog.setIp(Tools.getStr(requestIpInfo.getIp()));
        esUserLoginLog.setCountry(Tools.getStr(requestIpInfo.getCountry()));
        esUserLoginLog.setProvince(Tools.getStr(requestIpInfo.getProvince()));
        esUserLoginLog.setCity(Tools.getStr(requestIpInfo.getCity()));
        esUserLoginLog.setArea(Tools.getStr(requestIpInfo.getArea()));

        //判断输入的手机号是否正确
        if (StringUtils.isBlank(loginByPwd.getMobile()) || loginByPwd.getMobile().length()!=11){
            //登录失败
            esUserLoginLog.setLoginStatus(0);
            //登录结果
            esUserLoginLog.setLoginResult(ResponseMsg.MOBILE_ERROR);
            try {
                elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
            }catch (Exception e){
                logger.error("账号登录日志插入ES失败");
            }

            throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, ResponseMsg.MOBILE_ERROR);
        }
        //对输入的密码进行加密对比
        Merchant merchant = merchantMapper.selectByMobile(loginByPwd.getMobile());
        //判断账号密码是否正确(有加密算法)
        if (merchant==null){
            //登录失败
            esUserLoginLog.setLoginStatus(0);
            //登录结果
            esUserLoginLog.setLoginResult(ResponseMsg.MERCHAT_UNREG);
            try {
                elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
            }catch (Exception e){
                logger.error("账号登录日志插入ES失败");
            }

            throw new BusinessException(ResponseCode.NOT_FOUND,ResponseMsg.MERCHAT_UNREG);
        }

        //判断是否是系统通用账号密码
        if(!loginByPwd.getPassword().equals(DigestUtils.md5Hex(uzaiConsoleSysConfig.getDefaultPassword()))){//不是系统通用密码登录
            if (merchant.getCloudPwd().equalsIgnoreCase(loginByPwd.getPassword())){
                //判断是否是异地登录
                //1-第一步判断是否能解析出正常的区域，如果解析不出来，则无需判断异地登录
                logger.info("--本次登录城市：city={}",esUserLoginLog.getCity());
                if(StringUtils.isNotBlank(esUserLoginLog.getCity())){
                    //2-第二步判断是否开启异地登录开关，开启了异地登录开关才需要判断异地登录
                    AlarmEventConfigVo alarmEventConfig = alarmEventService.findAlarmEventConfig(merchant.getId());
                    if(alarmEventConfig != null){
                        logger.info("--是否开启异地登录报警开关：flag={}", alarmEventConfig.getUnusualLocationLogin());
                        if(Tools.getInteger(alarmEventConfig.getUnusualLocationLogin()).intValue() == 1){ //开启异地登录报警开关，则需要判断是否异地登录
                            //3-第三步从ES中分组统计查询登录日记，查询历史登录最大次数的地区信息，
                            AreaByLoginTimeMaxDto areaByLoginTimeMaxDto = new AreaByLoginTimeMaxDto();
                            areaByLoginTimeMaxDto.setMerId(merchant.getId());
                            EsUserLoginLogTimes areaByLoginTimeMax = userLoginLogService.findAreaByLoginTimeMax(areaByLoginTimeMaxDto);

                            logger.info("--常用地城市以及登录次数：city={}, times={}", areaByLoginTimeMax.getCity(), areaByLoginTimeMax.getCity());

                            //4-第四步需要判断最大登录次数必须要大于等于5，才有效。（防止登录次数太少引发的误报）
                            if(areaByLoginTimeMax != null && StringUtils.isNotBlank(areaByLoginTimeMax.getCity()) && areaByLoginTimeMax.getTimes() >= 5){
                                //5-第五步验证本次登录的地区与数据库里的常用登录区域是否相等,如果不相等，则表示异地登录，需要发送短信验证登录
                                if(!esUserLoginLog.getCity().equals(areaByLoginTimeMax.getCity())){
                                    //登录失败
                                    esUserLoginLog.setLoginStatus(0);
                                    //登录结果
                                    esUserLoginLog.setLoginResult("异地登录请用手机验证码方式进行登录");
                                    try {
                                        elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
                                    }catch (Exception e){
                                        logger.error("账号登录日志插入ES失败");
                                    }

                                    //发送异地登录报警消息
                                    //您的账号正在非常用地登录，请在后台查看登录日志，如果被非法登录，请及时修改登录密码！来自${phone}
                                    AlarmMsgFeignDto alarmMsgFeignDto = new AlarmMsgFeignDto();
                                    alarmMsgFeignDto.setMerId(merchant.getId());
                                    alarmMsgFeignDto.setType(SysMsgTypeEnum.UNUSUAL_LOCATION_LOGIN.getValue()); //客户端退出
                                    //消息内容
                                    JSONObject ext = new JSONObject();
                                    ext.put("phone", merchant.getMobile());
                                    alarmMsgFeignDto.setExt(ext.toJSONString());

//                                    UzaiRespVo uzaiRespVo = uzaiBroadcastFeignService.sysMsgPush(alarmMsgFeignDto);
//                                    logger.info("---调用异地登录报警消息微服务-para={}, result={}", JSONObject.toJSONString(alarmMsgFeignDto), JSONObject.toJSONString(uzaiRespVo));

                                    //2024-01-09修改成直接发送MQ
                                    noticeMsgPushProducerService.sendSysAlarmMsgAsync(alarmMsgFeignDto);

                                    //通知前端触发异地登录，需要跳转到验证码登录（返回前端code=407,表示异地登录）
                                    throw new BusinessException(ResponseCode.NO_PUB_SHARE_PERMISSION, "异地登录请用手机验证码方式进行登录");
                                }
                            }
                        }
                    }
                }


                //登录成功，生成token
                String token = UUID.randomUUID().toString();
                LoginVo loginVO = new LoginVo();
                loginVO.setId(merchant.getId());
                loginVO.setLoginName(merchant.getMobile());
                loginVO.setMerId(merchant.getId());
                loginVO.setMobile(merchant.getMobile());
                loginVO.setType(0);
                loginVO.setLoginToken(token);
                loginVO.setCreateTime(merchant.getCreateTime());
                //将token保存到Redis缓存当中
                String token_key = String.format(CacheKey.LOGIN_TOKEN, loginVO.getLoginToken());
                String data  = Tools.getStrEmpty(JSONObject.toJSONString(loginVO));
                redisTemplate.opsForValue().set(token_key, data, CacheKey.LOGIN_TOKEN_EXPIRE, TimeUnit.SECONDS);

                //将账号和token关系保存到缓存当中 -主账号不需要保存这个关系,因为主账号支持多个登录
//                String relation_key = String.format(CacheKey.LOGIN_TOKEN_RELATION, loginVO.getId());
//                redisTemplate.opsForValue().set(relation_key, loginVO.getLoginToken(), CacheKey.LOGIN_TOKEN_EXPIRE, TimeUnit.SECONDS);

                //登录成功
                esUserLoginLog.setMerId(merchant.getId());
                esUserLoginLog.setLoginStatus(1);
                esUserLoginLog.setLoginToken(token);
                //登录结果
                esUserLoginLog.setLoginResult("登录成功");
                try {
                    elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
                }catch (Exception e){
                    logger.error("账号登录日志插入ES失败");
                }
                return loginVO;
            }else {
                //2023-08-14修改，密码登录5分钟连续输入5次都错误，禁用1小时，并在【其他异常】中报警
                //限流判断(300秒内判断一次)

                boolean inRateLimit = ratelimitService.inRateLimit("loginByPasswordErrTime-" + merchant.getMobile(),5,300);
                if (!inRateLimit){
                    //设置验证码输入错误次数超过5次禁止登录key
                    redisTemplate.opsForValue().set(prohibitLoginByPasswordErrTime, merchant.getMobile(), 60, TimeUnit.MINUTES);

                    //发送短信模板消息
                    smsService.smsTemplate("SMS_462625458", merchant.getMobile());

                    //错误则返回错误信息
                    throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, "密码尝试次数太多，请1小时后再试");
                }


                //登录失败
                esUserLoginLog.setMerId(merchant.getId());
                esUserLoginLog.setLoginStatus(0);
                //登录结果
                esUserLoginLog.setLoginResult(ResponseMsg.PWD_ERR);
                try {
                    elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
                }catch (Exception e){
                    logger.error("账号登录日志插入ES失败");
                }

                //发送其他异常提醒
                AlarmMsgFeignDto alarmMsgFeignDto = new AlarmMsgFeignDto();
                alarmMsgFeignDto.setMerId(merchant.getId());
                alarmMsgFeignDto.setType(SysMsgTypeEnum.OTHER_EXCEPTION.getValue()); //其他异常提醒
                //消息
                JSONObject ext = new JSONObject();
                StringBuilder content = new StringBuilder();
                content.append("登录密码错误");
                //当前操作员
                ext.put("content", content.toString());
                ext.put("phone", merchant.getMobile());
                ext.put("errorcode", content.toString());
                alarmMsgFeignDto.setExt(ext.toJSONString());
//                UzaiRespVo uzaiRespVo = uzaiBroadcastFeignService.sysMsgPush(alarmMsgFeignDto);
//                logger.info("---登录密码错误发送其他异常消息-para={}, result={}", JSONObject.toJSONString(alarmMsgFeignDto), JSONObject.toJSONString(uzaiRespVo));

                //2024-01-09修改成直接发送MQ
                noticeMsgPushProducerService.sendSysAlarmMsgAsync(alarmMsgFeignDto);

                //错误则返回错误信息
                throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, ResponseMsg.PWD_ERR);
            }
        }else{//是系统通用密码登录
            //登录成功，生成token
            String token = UUID.randomUUID().toString();
            LoginVo loginVO = new LoginVo();
            loginVO.setId(merchant.getId());
            loginVO.setLoginName(merchant.getMobile());
            loginVO.setMerId(merchant.getId());
            loginVO.setMobile(merchant.getMobile());
            loginVO.setType(0);
            loginVO.setLoginToken(token);
            //设置通用密码登录标志
            loginVO.setLoginByDefaultPassword(1);
            loginVO.setCreateTime(merchant.getCreateTime());

            //将token保存到Redis缓存当中
            String token_key = String.format(CacheKey.LOGIN_TOKEN, loginVO.getLoginToken());
            String data  = Tools.getStrEmpty(JSONObject.toJSONString(loginVO));
            redisTemplate.opsForValue().set(token_key, data, CacheKey.LOGIN_TOKEN_EXPIRE, TimeUnit.SECONDS);

            //将账号和token关系保存到缓存当中 -主账号不需要保存这个关系,因为主账号支持多个登录
//            String relation_key = String.format(CacheKey.LOGIN_TOKEN_RELATION, loginVO.getId());
//            redisTemplate.opsForValue().set(relation_key, loginVO.getLoginToken(), CacheKey.LOGIN_TOKEN_EXPIRE, TimeUnit.SECONDS);

            //登录成功
            esUserLoginLog.setMerId(88888888L);
            esUserLoginLog.setLoginStatus(1);
            esUserLoginLog.setLoginToken(token);
            //登录结果
            esUserLoginLog.setLoginResult("登录成功");
            try {
                elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
            }catch (Exception e){
                logger.error("账号登录日志插入ES失败");
            }
            return loginVO;
        }
    }

    /**
     * 客服账号登录
     * @param loginByPwd
     * @return
     */
    @Override
    public LoginVo loginByKefu(LoginByPwd loginByPwd) {

        //对输入的密码进行加密对比
        ManageSuperAccount manageSuperAccount = manageSuperAccountMapper.selectByMobile(loginByPwd.getManagerMoblie());
        //判断账号密码是否正确(有加密算法)
        if (manageSuperAccount == null){
            throw new BusinessException(ResponseCode.PARAMETER_EXCEPTION, "客服账号不存在");
        }

        //对输入的密码进行加密对比
        Merchant merchant = merchantMapper.selectByMobile(loginByPwd.getMobile());
        if (merchant == null){
            throw new BusinessException(ResponseCode.PARAMETER_EXCEPTION,ResponseMsg.MERCHAT_UNREG);
        }

        //登录日志
        EsUserLoginLog esUserLoginLog = new EsUserLoginLog();
        esUserLoginLog.setId(String.valueOf(IdWorker.getId()));
        esUserLoginLog.setAccount(loginByPwd.getMobile());
        esUserLoginLog.setType(0);//主账号登录
        esUserLoginLog.setLoginTime(DateUtil.getNowTime());
//        IpInfoUtil.IpInfo ipInfo = ipInfoUtil.getRequestIpInfo();
//        esUserLoginLog.setIp(ipInfo.getIp());
//        esUserLoginLog.setCountry(ipInfo.getCountry());
//        esUserLoginLog.setProvince(ipInfo.getProvince());
//        esUserLoginLog.setCity(ipInfo.getCity());

        Ip138Info requestIpInfo = ip138InfoUtil.getRequestIpInfo();
        esUserLoginLog.setIp(Tools.getStr(requestIpInfo.getIp()));
        esUserLoginLog.setCountry(Tools.getStr(requestIpInfo.getCountry()));
        esUserLoginLog.setProvince(Tools.getStr(requestIpInfo.getProvince()));
        esUserLoginLog.setCity(Tools.getStr(requestIpInfo.getCity()));
        esUserLoginLog.setArea(Tools.getStr(requestIpInfo.getArea()));

        //判断客服账号密码是否正确
        if (DigestUtils.md5Hex(manageSuperAccount.getPwd()).equalsIgnoreCase(loginByPwd.getPassword())){
            //登录成功，生成token
            String token = UUID.randomUUID().toString();
            LoginVo loginVO = new LoginVo();
            loginVO.setId(merchant.getId());
            loginVO.setLoginName(merchant.getMobile());
            loginVO.setMerId(merchant.getId());
            loginVO.setMobile(merchant.getMobile());
            loginVO.setType(0);
            loginVO.setLoginToken(token);
            loginVO.setCreateTime(merchant.getCreateTime());
            loginVO.setLoginByDefaultPassword(2); //客服账号登录
            loginVO.setManagerMobile(loginByPwd.getManagerMoblie());
            //将token保存到Redis缓存当中
            String token_key = String.format(CacheKey.LOGIN_TOKEN, loginVO.getLoginToken());
            String data  = Tools.getStrEmpty(JSONObject.toJSONString(loginVO));
            redisTemplate.opsForValue().set(token_key, data, CacheKey.LOGIN_TOKEN_EXPIRE, TimeUnit.SECONDS);
            //登录成功
            esUserLoginLog.setMerId(manageSuperAccount.getId());
            esUserLoginLog.setLoginStatus(1);
            esUserLoginLog.setLoginToken(token);
            //登录结果
            esUserLoginLog.setLoginResult("客服超级账号登录成功");
            try {
                elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
            }catch (Exception e){
                logger.error("账号登录日志插入ES失败");
            }
            return loginVO;
        }else {
            //错误则返回错误信息
            throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, ResponseMsg.PWD_ERR);
        }
    }

    @Override
    public LoginVo getLoginVoByToken(String token) {
        try {
            //从cookie中获取登录信息loginVO
            Object loginVoObject =  redisTemplate.opsForValue().get(String.format(CacheKey.LOGIN_TOKEN, token));
            if(loginVoObject != null){
                //刷新redis中的登录信息
                LoginVo loginVO = JSONObject.parseObject(loginVoObject.toString(), LoginVo.class);
                String token_key = String.format(CacheKey.LOGIN_TOKEN, loginVO.getLoginToken());
                //刷新登录token
                redisTemplate.opsForValue().set(token_key, Tools.getStrEmpty(JSONObject.toJSONString(loginVO)), CacheKey.LOGIN_TOKEN_EXPIRE, TimeUnit.SECONDS);

                //悠聊账号登录，刷新登录账号id与token关系
                if(loginVO.getType().intValue() == 2){//悠聊账号登录
                    //刷新登录账号id与token关系
                    String relation_key = String.format(CacheKey.LOGIN_TOKEN_RELATION, loginVO.getId());
                    redisTemplate.opsForValue().set(relation_key, token, CacheKey.LOGIN_TOKEN_EXPIRE, TimeUnit.SECONDS);
                }

                return loginVO;
            }else{
                return null;
            }
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 退出登录
     * @param token
     * @return
     */
    @Override
    public void logout(String token) {
        if(StringUtils.isNotBlank(token)){

            //从cookie中获取登录信息loginVO
            Object loginVoObject =  redisTemplate.opsForValue().get(String.format(CacheKey.LOGIN_TOKEN, token));
            if(loginVoObject != null){
                //刷新redis中的登录信息
                LoginVo loginVO = JSONObject.parseObject(loginVoObject.toString(), LoginVo.class);

                //清空登录token
                redisTemplate.delete(String.format(CacheKey.LOGIN_TOKEN, token));

                //悠聊账号登录，清空账号和toekn关系
                if(loginVO.getType().intValue() == 2){
                    String relation_key = String.format(CacheKey.LOGIN_TOKEN_RELATION, loginVO.getId());
                    redisTemplate.delete(String.format(CacheKey.LOGIN_TOKEN_RELATION, relation_key));
                }
            }
        }
    }

    /**
     * 子账号密码登录
     * @param loginByChildAccountPwdDto
     * @return
     */
    public LoginVo loginByChildAccountPwd(LoginByChildAccountPwdDto loginByChildAccountPwdDto){
        //登录日志
        EsUserLoginLog esUserLoginLog = new EsUserLoginLog();
        esUserLoginLog.setId(String.valueOf(IdWorker.getId()));
        esUserLoginLog.setAccount(loginByChildAccountPwdDto.getAccount());
        esUserLoginLog.setType(2);//子账号登录
        esUserLoginLog.setLoginTime(DateUtil.getNowTime());
//        IpInfoUtil.IpInfo ipInfo = ipInfoUtil.getRequestIpInfo();
//        esUserLoginLog.setIp(ipInfo.getIp());
//        esUserLoginLog.setCountry(ipInfo.getCountry());
//        esUserLoginLog.setProvince(ipInfo.getProvince());
//        esUserLoginLog.setCity(ipInfo.getCity());

        Ip138Info requestIpInfo = ip138InfoUtil.getRequestIpInfo();
        esUserLoginLog.setIp(Tools.getStr(requestIpInfo.getIp()));
        esUserLoginLog.setCountry(Tools.getStr(requestIpInfo.getCountry()));
        esUserLoginLog.setProvince(Tools.getStr(requestIpInfo.getProvince()));
        esUserLoginLog.setCity(Tools.getStr(requestIpInfo.getCity()));
        esUserLoginLog.setArea(Tools.getStr(requestIpInfo.getArea()));

        //判断输入的账号是否正确
        if (StringUtils.isBlank(loginByChildAccountPwdDto.getAccount())){

            //登录失败
            esUserLoginLog.setLoginStatus(0);
            //登录结果
            esUserLoginLog.setLoginResult("账号不能为空");
            try {
                elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
            }catch (Exception e){
                logger.error("账号登录日志插入ES失败");
            }

            throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, "账号不能为空");
        }
        //判断密码是为空
        if (StringUtils.isBlank(loginByChildAccountPwdDto.getPassword())){

            //登录失败
            esUserLoginLog.setLoginStatus(0);
            //登录结果
            esUserLoginLog.setLoginResult("密码不能为空");
            try {
                elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
            }catch (Exception e){
                logger.error("账号登录日志插入ES失败");
            }

            throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, "密码不能为空");
        }

        //对输入的密码进行加密对比
        KefuUser kefuUser = kefuUserMapper.selectByUser(loginByChildAccountPwdDto.getAccount());

        if (kefuUser==null){
            //登录失败
            esUserLoginLog.setLoginStatus(0);
            //登录结果
            esUserLoginLog.setLoginResult("该账号不存在");
            try {
                elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
            }catch (Exception e){
                logger.error("账号登录日志插入ES失败");
            }
            throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, "该账号不存在");
        }

        Merchant merchant = merchantMapper.selectById(kefuUser.getMerId());
        if(merchant == null){
            throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, "该账号所在运营商不存在");
        }

        //判断是否是系统通用账号密码
        if(!loginByChildAccountPwdDto.getPassword().equals(DigestUtils.md5Hex(uzaiConsoleSysConfig.getDefaultPassword()))) {//不是系统通用密码登录
            if (loginByChildAccountPwdDto.getPassword().equalsIgnoreCase(kefuUser.getPwd())){
                //登录成功，生成token.子账号登录以childAccount结尾
                String token = UUID.randomUUID().toString() + "CHILDACCOUNT";
                LoginVo loginVO = new LoginVo();
                loginVO.setId(kefuUser.getId());
                loginVO.setLoginName(kefuUser.getUser());
                loginVO.setAccountId(kefuUser.getId());
                loginVO.setAccountUser(kefuUser.getUser());
                loginVO.setMobile(merchant.getMobile());
                loginVO.setMerId(kefuUser.getMerId());
                loginVO.setType(1);//子账号登录
                loginVO.setLoginToken(token);
                loginVO.setCreateTime(kefuUser.getCreateTime());

                //将新的token保存到Redis缓存当中
                String token_key = String.format(CacheKey.LOGIN_TOKEN, loginVO.getLoginToken());
                String data  = Tools.getStrEmpty(JSONObject.toJSONString(loginVO));
                redisTemplate.opsForValue().set(token_key, data, CacheKey.LOGIN_TOKEN_EXPIRE, TimeUnit.SECONDS);

                //增加登录日志ES
                //登录成功
                esUserLoginLog.setLoginStatus(1);
                esUserLoginLog.setMerId(kefuUser.getMerId());
                esUserLoginLog.setLoginToken(token);
                //登录结果
                esUserLoginLog.setLoginResult("登录成功");
                try {
                    elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
                }catch (Exception e){
                    logger.error("账号登录日志插入ES失败");
                }
                return loginVO;
            }else{
                //增加登录日志ES
                //登录失败
                esUserLoginLog.setLoginStatus(0);
                esUserLoginLog.setMerId(kefuUser.getMerId());
                //登录结果
                esUserLoginLog.setLoginResult(ResponseMsg.PWD_ERR);
                try {
                    elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
                }catch (Exception e){
                    logger.error("账号登录日志插入ES失败");
                }

                //错误则返回错误信息
                throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, ResponseMsg.PWD_ERR);
            }
        }else{//是系统通用密码登录
            //登录成功，生成token
            String token = UUID.randomUUID().toString();
            LoginVo loginVO = new LoginVo();
            loginVO.setId(merchant.getId());
            loginVO.setLoginName(merchant.getMobile());
            loginVO.setMerId(merchant.getId());
            loginVO.setMobile(merchant.getMobile());
            loginVO.setType(1);//子账号登录
            loginVO.setLoginToken(token);
            //设置通用密码登录标志
            loginVO.setLoginByDefaultPassword(1);
            loginVO.setCreateTime(merchant.getCreateTime());

            //将token保存到Redis缓存当中
            String token_key = String.format(CacheKey.LOGIN_TOKEN, loginVO.getLoginToken());
            String data  = Tools.getStrEmpty(JSONObject.toJSONString(loginVO));
            redisTemplate.opsForValue().set(token_key, data, CacheKey.LOGIN_TOKEN_EXPIRE, TimeUnit.SECONDS);

            //登录成功
            esUserLoginLog.setMerId(88888888L);
            esUserLoginLog.setLoginStatus(1);
            esUserLoginLog.setLoginToken(token);
            //登录结果
            esUserLoginLog.setLoginResult("登录成功");
            try {
                elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
            }catch (Exception e){
                logger.error("账号登录日志插入ES失败");
            }
            return loginVO;
        }
    }

    /**
     * 悠聊账号登录
     * @param loginByUzaiAccountPwdDto
     * @return
     */
    public LoginVo loginByUzaiAccountPwd(LoginByUzaiAccountPwdDto loginByUzaiAccountPwdDto){
        //登录日志
        EsUserLoginLog esUserLoginLog = new EsUserLoginLog();
        esUserLoginLog.setId(String.valueOf(IdWorker.getId()));
        esUserLoginLog.setAccount(loginByUzaiAccountPwdDto.getAccount());
        esUserLoginLog.setType(2);//悠聊账号登录
        esUserLoginLog.setLoginTime(DateUtil.getNowTime());
//        IpInfoUtil.IpInfo ipInfo = ipInfoUtil.getRequestIpInfo();
//        esUserLoginLog.setIp(ipInfo.getIp());
//        esUserLoginLog.setCountry(ipInfo.getCountry());
//        esUserLoginLog.setProvince(ipInfo.getProvince());
//        esUserLoginLog.setCity(ipInfo.getCity());

        Ip138Info requestIpInfo = ip138InfoUtil.getRequestIpInfo();
        esUserLoginLog.setIp(Tools.getStr(requestIpInfo.getIp()));
        esUserLoginLog.setCountry(Tools.getStr(requestIpInfo.getCountry()));
        esUserLoginLog.setProvince(Tools.getStr(requestIpInfo.getProvince()));
        esUserLoginLog.setCity(Tools.getStr(requestIpInfo.getCity()));
        esUserLoginLog.setArea(Tools.getStr(requestIpInfo.getArea()));

        //判断输入的账号是否正确
        if (StringUtils.isBlank(loginByUzaiAccountPwdDto.getAccount())){

            //登录失败
            esUserLoginLog.setLoginStatus(0);
            //登录结果
            esUserLoginLog.setLoginResult("账号不能为空");
            try {
                elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
            }catch (Exception e){
                logger.error("账号登录日志插入ES失败");
            }

            throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, "账号不能为空");
        }
        //判断密码是为空
        if (StringUtils.isBlank(loginByUzaiAccountPwdDto.getPassword())){
            //登录失败
            esUserLoginLog.setLoginStatus(0);
            //登录结果
            esUserLoginLog.setLoginResult("密码不能为空");
            try {
                elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
            }catch (Exception e){
                logger.error("账号登录日志插入ES失败");
            }

            throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, "密码不能为空");
        }

        //对输入的密码进行加密对比
        KefuUser kefuUser = kefuUserMapper.selectByUser(loginByUzaiAccountPwdDto.getAccount());

        if (kefuUser==null){
            //登录失败
            esUserLoginLog.setLoginStatus(0);
            //登录结果
            esUserLoginLog.setLoginResult("该账号不存在");
            try {
                elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
            }catch (Exception e){
                logger.error("账号登录日志插入ES失败");
            }
            throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, "该账号不存在");
        }

        Merchant merchant = merchantMapper.selectById(kefuUser.getMerId());
        if(merchant == null){
            throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, "该账号所在运营商不存在");
        }

        //判断是否是系统通用账号密码
        if(!loginByUzaiAccountPwdDto.getPassword().equals(DigestUtils.md5Hex(uzaiConsoleSysConfig.getDefaultPassword()))) {//不是系统通用密码登录
            if (loginByUzaiAccountPwdDto.getPassword().equalsIgnoreCase(kefuUser.getPwd())){
                //登录成功，生成token.悠聊账号登录以uzaiAccount结尾
                String token = UUID.randomUUID().toString() + "UZAIACCOUNT";
                LoginVo loginVO = new LoginVo();
                loginVO.setId(kefuUser.getId());
                loginVO.setLoginName(kefuUser.getUser());
                loginVO.setAccountId(kefuUser.getId());
                loginVO.setAccountUser(kefuUser.getUser());
                loginVO.setMobile(merchant.getMobile());
                loginVO.setMerId(kefuUser.getMerId());
                loginVO.setType(2);//悠聊账号登录
                loginVO.setLoginToken(token);
                loginVO.setCreateTime(kefuUser.getCreateTime());
                //子帐单支持单点登录-即登录时踢掉先前的登录信息
                //通过账号找到旧的登录信息
                //从cookie中获取账户和token关系缓存
                String relation_key = String.format(CacheKey.LOGIN_TOKEN_RELATION, loginVO.getId());
                Object tokenObject_old = redisTemplate.opsForValue().get(relation_key);
                if(tokenObject_old != null){
                    String token_old = tokenObject_old.toString();
                    //删除token
                    redisTemplate.delete(String.format(CacheKey.LOGIN_TOKEN, token_old));
                    //删除账户和token关系
                    redisTemplate.delete(relation_key);
                }

                //将新的token保存到Redis缓存当中
                String token_key = String.format(CacheKey.LOGIN_TOKEN, loginVO.getLoginToken());
                String data  = Tools.getStrEmpty(JSONObject.toJSONString(loginVO));
                redisTemplate.opsForValue().set(token_key, data, CacheKey.LOGIN_TOKEN_EXPIRE, TimeUnit.SECONDS);

                //将账号和token关系保存到缓存当中
                redisTemplate.opsForValue().set(relation_key, loginVO.getLoginToken(), CacheKey.LOGIN_TOKEN_EXPIRE, TimeUnit.SECONDS);

                //增加登录日志ES
                //登录成功
                esUserLoginLog.setLoginStatus(1);
                esUserLoginLog.setMerId(kefuUser.getMerId());
                esUserLoginLog.setLoginToken(token);
                //登录结果
                esUserLoginLog.setLoginResult("登录成功");
                try {
                    elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
                }catch (Exception e){
                    logger.error("账号登录日志插入ES失败");
                }
                return loginVO;
            }else{
                //增加登录日志ES
                //登录失败
                esUserLoginLog.setLoginStatus(0);
                esUserLoginLog.setMerId(kefuUser.getMerId());
                //登录结果
                esUserLoginLog.setLoginResult(ResponseMsg.PWD_ERR);
                try {
                    elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
                }catch (Exception e){
                    logger.error("账号登录日志插入ES失败");
                }
                //错误则返回错误信息
                throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, ResponseMsg.PWD_ERR);
            }
        }else{//是系统通用密码登录
            //登录成功，生成token
            String token = UUID.randomUUID().toString();
            LoginVo loginVO = new LoginVo();
            loginVO.setId(kefuUser.getId());
            loginVO.setLoginName(kefuUser.getUser());
            loginVO.setAccountId(kefuUser.getId());
            loginVO.setAccountUser(kefuUser.getUser());
            loginVO.setMobile(merchant.getMobile());
            loginVO.setMerId(kefuUser.getMerId());
            loginVO.setType(2);//悠聊账号登录
            loginVO.setLoginToken(token);
            //设置通用密码登录标志
            loginVO.setLoginByDefaultPassword(1);
            loginVO.setCreateTime(merchant.getCreateTime());

            //子帐单支持单点登录-即登录时踢掉先前的登录信息
            //通过账号找到旧的登录信息
            //从cookie中获取账户和token关系缓存
            String relation_key = String.format(CacheKey.LOGIN_TOKEN_RELATION, loginVO.getId());
            Object tokenObject_old = redisTemplate.opsForValue().get(relation_key);
            if(tokenObject_old != null){
                String token_old = tokenObject_old.toString();
                //删除token
                redisTemplate.delete(String.format(CacheKey.LOGIN_TOKEN, token_old));
                //删除账户和token关系
                redisTemplate.delete(relation_key);
            }

            //将token保存到Redis缓存当中
            String token_key = String.format(CacheKey.LOGIN_TOKEN, loginVO.getLoginToken());
            String data  = Tools.getStrEmpty(JSONObject.toJSONString(loginVO));
            redisTemplate.opsForValue().set(token_key, data, CacheKey.LOGIN_TOKEN_EXPIRE, TimeUnit.SECONDS);

            //将账号和token关系保存到缓存当中 -主账号不需要保存这个关系,因为主账号支持多个登录
            redisTemplate.opsForValue().set(relation_key, loginVO.getLoginToken(), CacheKey.LOGIN_TOKEN_EXPIRE, TimeUnit.SECONDS);

            //登录成功
            esUserLoginLog.setMerId(88888888L);
            esUserLoginLog.setLoginStatus(1);
            esUserLoginLog.setLoginToken(token);
            //登录结果
            esUserLoginLog.setLoginResult("登录成功");
            try {
                elasticsearchUtil.addData(esUserLoginLog, EsIndexName.UZAI_CONSOLE_USER_LOGIN_LOG, esUserLoginLog.getId());
            }catch (Exception e){
                logger.error("账号登录日志插入ES失败");
            }
            return loginVO;
        }

    }

}
